#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import print_function

import sqlite3

import pymysql
from xjlibrary.tools import BaseDict

from .database_factory import SqlFactory
from .sqlmoudle import SqlMoudle, Sqlite3Moudle


class Mysql(SqlFactory):

    @property
    def db(self):
        # 就是conn
        if not self.is_ping():
            assert self._mysqlmoudle, AttributeError("MySQL model不存在 无法重新连接 请设置moudle")
            self.reConnect()
        return self._db  # type: pymysql.cursors.Cursor

    @db.setter
    def db(self, value):
        self._db = value

    @property
    def cursor(self):
        if self._cursor.connection:
            return self._cursor  # type: pymysql.cursors.Cursor
        else:
            self._cursor = self._db.cursor()
            return self._cursor

    @cursor.setter
    def cursor(self, value):
        self._cursor = value

    @property
    def mysqlmoudle(self):
        return self._mysqlmoudle  # type: SqlMoudle

    @mysqlmoudle.setter
    def mysqlmoudle(self, moudle):
        self._mysqlmoudle = moudle

    def get_new_cursor(self):
        """
        获取一个新的游标
        :return:
        """
        # 检查db的存在及是否断掉 不知道为什么 不可以加括号 但编辑器会警告
        return self._db.cursor()

    def link(self, mysqlmoudle: SqlMoudle):
        """
        连接数据库
        :param mysqlmoudle:
        :return:
        """
        self._mysqlmoudle = mysqlmoudle
        # 返回连接对象
        self._db = pymysql.connect(**mysqlmoudle.to_dict())
        self._cursor = self._db.cursor()

        return self

    def reConnect(self):
        """
        重新连接数据库
        :return:
        """
        try:
            self._db.ping()
            self._db.close()
        except:
            self.link(self._mysqlmoudle)
        self.link(self._mysqlmoudle)

    def is_ping(self):
        if self._db:
            try:
                return self._db.ping()
            except:
                self.link(self._mysqlmoudle)
        else:
            raise AttributeError("数据库连接对象不存在，请调用reConnect重新连接")

    def commit(self):
        """
        事务提交
        :return:
        """
        # 对象判断
        assert isinstance(self._db, pymysql.connections.Connection)
        self._db.commit()

    def execute(self, sql):
        """
        执行sql语句
        :param sql:
        :return:
        """
        try:
            assert isinstance(self._cursor, pymysql.cursors.Cursor)
            # 游标是否被关闭,执行该函数保证有游标
            self._cursor.execute(sql)
            self.commit()
        except Exception as e:
            raise e

    def fetchall(self):
        """
        select 后调用获取全部返回结果
        :return:
        """
        result = self._cursor.fetchall()
        return result

    def close(self):
        assert isinstance(self._db, pymysql.connections.Connection)
        self._db.close()

    def rollback(self):
        self.db.rollback()

    def escape(self, strings):
        return self.db.escape(strings)

    def __repr__(self):
        return 'Mysql().link(%s)' % self._mysqlmoudle

    def __str__(self):
        return "Mysql %s" % self._mysqlmoudle
    
    
    def __del__(self):
        self.close()


class Sqlite3(SqlFactory):
    def __init__(self):
        # 就是conn
        self.db = ''
        self.cursor = ''
        self.sqlite3_moudle = {}

    def set_encoding(self, bytes=False, encoding="gbk"):
        """
        https://blog.csdn.net/xkxjy/article/details/8179479
        在不知道编码格式的情况下 使用bytes 但我没有成功
        :param bytes:
        :param encoding:
        :return:
        """
        # if bytes:
        #     self.db.text_factory = bytes
        # else:
        #     self.db.text_factory = lambda x: str(x, encoding, 'ignore')
        self.db.text_factory = lambda x: str(x, encoding, 'ignore')

    def link(self, sqlite3_moudle: Sqlite3Moudle):
        """
        连接数据库
        :param sqlite3_moudle:
        :param mysqlmoudle:
        :return:
        """
        self.sqlite3_moudle = sqlite3_moudle
        # 返回连接对象
        dicts = sqlite3_moudle.to_dict()
        dicts = BaseDict.removeDictsNone(dicts)
        self.db = sqlite3.connect(**dicts)
        self.cursor = self.db.cursor()
        return self.db, self.cursor

    def get_cursor(self) -> sqlite3.Cursor:
        """
        本函数通过链接返回sqlite3的游标
        :param connect: 传入的是sqlite3的连接
        :return: 返回游标
        """
        assert isinstance(self.db, sqlite3.Connection)
        self.cursor = self.db.cursor()
        return self.cursor

    def execute(self, sql: str) -> sqlite3.Cursor:
        """
        通过游标对象执行sql语句并返回结果
        :param cursor: 游标对象
        :param sql:  需要执行的sql语句
        :return:  返回结果的游标对象
        """
        assert isinstance(self.cursor, sqlite3.Cursor)
        result = self.cursor.execute(sql)
        return result

    def executemany(self, sql: str, seq_of_parameters) -> sqlite3.Cursor:
        assert isinstance(self.cursor, sqlite3.Cursor)
        result = self.cursor.executemany(sql, seq_of_parameters)
        return result

    def get_all_field_info(self, tablename):
        sql = "PRAGMA table_info({})".format(tablename)
        result = self.execute(sql)
        return result.fetchall()

    def get_all_field(self, tablename):
        listinfo = self.get_all_field_info(tablename)
        return [row[1] for row in listinfo]

    def get_rowcount(self, ) -> int:
        """
        获取sql语句影响的行数
        :param cursor: 游标对象
        :return:  影响的行数
        """
        assert isinstance(self.cursor, sqlite3.Cursor)
        num = self.cursor.rowcount
        return num

    def close_cursor(self) -> None:
        """
        关闭游标
        :param cursor: 游标
        :return: None
        """
        assert isinstance(self.cursor, sqlite3.Cursor)
        self.cursor.close()

    def my_commit(self) -> None:
        """
        提交
        :param connect: 连接
        :return: None
        """
        assert isinstance(self.db, sqlite3.Connection)
        self.db.commit()

    def close_connect(self) -> None:
        """
        链接关闭
        :param connect: 连接
        :return: None
        """
        assert isinstance(self.db, sqlite3.Connection)
        self.db.close()
        
    
    def __del__(self):
        self.close_connect()
        
